using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;

namespace Nate.Shared
{
    /// <summary>
    /// Converts bitmap into a list of Nodes that a graph object can use. 
    /// </summary>
    public class BitmapToGraph
    {
        public BitmapToGraph(Bitmap bmp, Color non_traverseable, bool allow_diagonals, bool allow_variable)
        {
            _non_traverseable = non_traverseable.ToArgb();
            _rows             = bmp.Height;
            _columns          = bmp.Width;
            _traversableNodes = new List<Pathfinding.Node>();

            _Allocate(bmp);
            _Stitch(bmp, allow_diagonals, allow_variable);
        }

        public List<Pathfinding.Node> GraphNodes { get { return _traversableNodes; } }

        #region Private Methods
        private void _AddNeighbor(int y, int x, Pathfinding.Node neighbor, bool allow_variable)
        {
            Pathfinding.Node node = _allNodes[y, x];
            if (null == node)
            {
                return;
            }

            double cost_scalar = 1.0;
            double cost_to_neighbor = 1.0;
            if (allow_variable)
            {
                cost_to_neighbor = (float)((uint)_non_traverseable - uint.MaxValue) / (float)uint.MaxValue;
                cost_to_neighbor *= cost_scalar;
            }

            node.Neighbors.Add( cost_to_neighbor, neighbor);
        }

        private void _Allocate(Bitmap bmp)
        {
            _allNodes = new Pathfinding.Node[_rows, _columns];

            for (int y = 0; y < _rows; ++y)
            {
                for (int x = 0; x < _columns; ++x)
                {
                    Color clr = bmp.GetPixel(x, y);
                    if (clr.ToArgb() == _non_traverseable)
                    {
                        continue;
                    }

                    Pathfinding.Node node = new Pathfinding.Node(x, y, _traversableNodes.Count, (uint)clr.ToArgb());
                    _traversableNodes.Add(node);
                    _allNodes[y, x] = node;
                }
            }
        }

        private void _Stitch(Bitmap bmp, bool allow_diagonals, bool allow_variable)
        {
            foreach (Pathfinding.Node node in _traversableNodes)
            {
                int y = node.Y;
                int x = node.X;

                //  Above
                if (y - 1 >= 0)
                    _AddNeighbor(y - 1, x, node, allow_variable);
                //  Right
                if (x + 1 < bmp.Width)
                    _AddNeighbor(y, x + 1, node, allow_variable);
                //  Left
                if (x - 1 >= 0)
                    _AddNeighbor(y, x - 1, node, allow_variable);
                //  Below
                if (y + 1 < bmp.Height)
                    _AddNeighbor(y + 1, x, node, allow_variable);
                
                // Diagonals!
                if (allow_diagonals)
                {
                    //  Above/Right
                    if (y - 1 >= 0 && x + 1 < bmp.Width)
                        _AddNeighbor(y - 1, x + 1, node, allow_variable);
                    //  Above/Left
                    if (y - 1 >= 0 && x - 1 >= 0)
                        _AddNeighbor(y - 1, x - 1, node, allow_variable);
                    //  Below/Left
                    if (y + 1 < bmp.Height && x - 1 >= 0)
                        _AddNeighbor(y + 1, x - 1, node, allow_variable);
                    //  Below/Right
                    if (y + 1 < bmp.Height && x + 1 < bmp.Width)
                        _AddNeighbor(y + 1, x + 1, node, allow_variable);
                }                 
            }
        }
        
        #endregion

        #region Private Data

        Pathfinding.Node[,]     _allNodes;
        List<Pathfinding.Node>  _traversableNodes;
        int                     _rows;
        int                     _columns;
        int                     _non_traverseable;

        #endregion
    }
}
